use std::collections::HashSet;

use anyhow::Context;
use axum::extract::State;
use axum::http::request::Parts;
use axum::response::IntoResponse;
use serde::{Deserialize, Serialize};
use tracing::instrument;
use validator::Validate;

use crate::app_req::ValidatedJsonBody;
use crate::app_resp::{AppError, AppResult};
use crate::AppState;
use crate::auth::{AuthEndpoint, AuthUser};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
    pub id: u64,
    pub username: String,
    pub password: String,
    pub phone: String,
    pub roles: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Claims {
    // 用户名
    pub sub: String,
    // 过期时间
    pub exp: u64,
    // 启用时间
    pub nbf: u64,
    // 接收jwt的一方
    pub aud: String,
    //  jwt签发者
    pub iss: String,
    pub user_id: u64,
    pub phone: String,
    pub roles: String,
}

impl AuthUser for Claims {
    fn get_permission(&self) -> HashSet<String> {
        self.roles.split(',').map(|s| s.to_string()).collect()
    }
}

#[derive(Debug, Clone, Default)]
pub struct RestAuthEndpoint;

impl AuthEndpoint for RestAuthEndpoint {
    fn auth_fail(&self, parts: &mut Parts) -> impl IntoResponse {
        AppError::Unauthorized("请先进行身份验证".into())
    }
}

#[derive(Debug, Clone, Deserialize, Validate)]
pub struct LoginParam {
    #[validate(length(min = 1, message = "Can not be empty"))]
    username: String,
    #[validate(length(min = 1, message = "Can not be empty"))]
    password: String,
}

#[derive(Debug, Serialize)]
pub struct LoginResp {
    username: String,
    token: String,
}

#[instrument]
pub async fn login(
    // session: Session,
    // cookies: Cookies,
    state: State<AppState>,
    ValidatedJsonBody(login_param): ValidatedJsonBody<LoginParam>,
) -> AppResult<LoginResp> {
    if login_param.username == "emmm" {
        let current_timestamp = jsonwebtoken::get_current_timestamp();
        let exp = current_timestamp + 60 * 60 * 24 * 7;
        let token = jsonwebtoken::encode(
            &state.jwt_info.header,
            &Claims {
                sub: login_param.username.clone(),
                exp,
                nbf: current_timestamp,
                aud: "APP".to_string(),
                iss: "AXUM".to_string(),
                user_id: 0,
                phone: "phone".to_string(),
                roles: "A,B,C".to_string(),
            }, &state.jwt_info.encoding_key).context("jwt编码错误")?;
        return Ok(LoginResp {
            username: login_param.username,
            token,
        }.into());
    }
    Err(AppError::Unauthorized("用户名或密码错误".to_string()))
}